﻿using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Xml;
using BeeCoders.Tools.ControlCanvas.Adorners;
using BeeCoders.Tools.ControlCanvas.Infrastructure;

namespace BeeCoders.Tools.ControlCanvas
{
	public class DesignerCanvas : Canvas
	{
		#region ctor
		public DesignerCanvas()
		{
			IsSnapToGrid = false;
			IsInDesignMode = false;
			ShowGridLines = false;

			Loaded += DesignerCanvasLoaded;

			Controls = new ControlCollection();

			Controls.CollectionChanged += ItemsSourceCollectionChanged;
		}
		#endregion

		void ItemsSourceCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			if (e.NewItems != null)
			{
				foreach (var control in e.NewItems)
				{
					DesignerItem newNode = ((ControlViewModel)control).BuildNode(IsInDesignMode);

					Children.Add(newNode);
				}
				
			}

			if (e.OldItems != null)
			{
				foreach (var control in e.OldItems)
				{
					Children.Remove(((ControlViewModel)control).Node);
				}

			}
			
		}

		private void DesignerCanvasLoaded(object sender, RoutedEventArgs e)
		{
			DrawGridLines(true);
		}

	  
		private Point? dragStartPoint;

		public IEnumerable<ControlViewModel> SelectedControls
		{
			get
			{
				var selectedItems = from item in Children.OfType<DesignerItem>()
									where item.IsSelected
									select item.Control;

				return selectedItems;
			}
		}

		public IEnumerable<DesignerItem> SelectedItems
		{
			get
			{
				var selectedItems = from item in Children.OfType<DesignerItem>()
									where item.IsSelected
									select item;

				return selectedItems;
			}
		}

		#region Debug
		public void DumpDebug()
		{
			
			//      Console.WriteLine(@"Report Controls:");

			//foreach (var reportControl in ReportControls)
			//{
			//   reportControl.DumpDebug();
			//}

			//Console.WriteLine(@"Designer Controls:");

			//foreach (var reportControl in DesignerControls)
			//{
			//    reportControl.DumpDebug();
			//}
		}
		#endregion


		#region ItemsSource
		public ControlCollection Controls
		{
			get { return (ControlCollection)GetValue(ControlsProperty); }
			set
			{
				SetValue(ControlsProperty, value);

			}
		}

		public static readonly DependencyProperty ControlsProperty = DependencyProperty.Register("Controls", typeof(ControlCollection), typeof(DesignerCanvas), new FrameworkPropertyMetadata(null, OnControlsChanged));

		private static void OnControlsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{

			var canvas = d as DesignerCanvas;
			if (canvas == null) return;
			if (canvas.Controls == null) return;

			canvas.Children.Clear();

			foreach (var control in canvas.Controls)
			{
				DesignerItem newNode = control.BuildNode(canvas.IsInDesignMode);

				canvas.Children.Add(newNode);
			}
		}

	   
		#endregion

		#region IsInDesignMode
		public bool IsInDesignMode
		{
			get { return (bool)GetValue(IsInDesignModeProperty); }
			set
			{
				SetValue(IsInDesignModeProperty, value);

			}
		}

		public static readonly DependencyProperty IsInDesignModeProperty = DependencyProperty.Register("IsInDesignMode", typeof(bool), typeof(DesignerCanvas), new FrameworkPropertyMetadata(false,OnIsInDesignModeChanged));

		public void SetDesignMode(bool isInDesignMode)
		{
			if (Controls == null) return;

			foreach (var control in Controls)
			{
				control.IsInDesign = isInDesignMode;
			}


		   DeselectAll();

		}

		private static void OnIsInDesignModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((DesignerCanvas)d).SetDesignMode((bool)e.NewValue);
			foreach (var control in ((DesignerCanvas)d).Controls)
			{
				control.IsInDesign = (bool)e.NewValue;
			}
		}

		#endregion

		#region IsLinkedControlSelection
		public bool IsLinkedControlSelection
		{
			get { return (bool)GetValue(IsLinkedControlSelectionProperty); }
			set
			{
				SetValue(IsLinkedControlSelectionProperty, value);

			}
		}

		public static readonly DependencyProperty IsLinkedControlSelectionProperty = DependencyProperty.Register("IsLinkedControlSelection", typeof(bool), typeof(DesignerCanvas), new FrameworkPropertyMetadata(false, OnIsLinkedControlSelectionChanged));

		private static void OnIsLinkedControlSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
		   
		}

		#endregion

		#region Grid
		#region ShowGridLines
		public bool ShowGridLines
		{
			get { return (bool)GetValue(ShowGridLinesProperty); }
			set
			{
				SetValue(ShowGridLinesProperty, value);

				IEnumerable<Line> collection = Children.OfType<Line>();

				foreach (var line in collection)
				{
					if (value == false)
					{
						line.Visibility = Visibility.Hidden;
					}
					else
					{
						line.Visibility = Visibility.Visible;
					}
				}
			}
		}

		public static readonly DependencyProperty ShowGridLinesProperty = DependencyProperty.Register("ShowGridLines", typeof(bool), typeof(DesignerCanvas), new FrameworkPropertyMetadata(false, OnShowGridLinesChanged));


		private static void OnShowGridLinesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			IEnumerable<Line> collection = ((DesignerCanvas) d).Children.OfType<Line>();

			foreach (var line in collection)
			{
				if ((bool)e.NewValue == false)
				{
					line.Visibility = Visibility.Hidden;
				}
				else
				{
					line.Visibility = Visibility.Visible;
				}
			}
		}
		#endregion

		#region IsSnapToGrid
		public bool IsSnapToGrid
		{
			get { return (bool)GetValue(IsSnapToGridProperty); }
			set { SetValue(IsSnapToGridProperty, value); }
		}

		public static readonly DependencyProperty IsSnapToGridProperty = DependencyProperty.Register("IsSnapToGrid", typeof(bool), typeof(DesignerCanvas), new FrameworkPropertyMetadata(false, OnSnapToGridChanged));

		private static void OnSnapToGridChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
		}
		#endregion

		#region SnapOffsetX
		public int SnapOffsetX
		{
			get { return (int)GetValue(SnapOffsetXProperty); }
			set { SetValue(SnapOffsetXProperty, value); }
		}

		public static readonly DependencyProperty SnapOffsetXProperty = DependencyProperty.Register("SnapOffsetX", typeof(int), typeof(DesignerCanvas), new FrameworkPropertyMetadata(10,OnSnapOffsetXChanged));

		private static void OnSnapOffsetXChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{

			((DesignerCanvas) d).DrawGridLines(true);
		}
		#endregion

		#region SnapOffsetY
		public int SnapOffsetY
		{
			get { return (int)GetValue(SnapOffsetYProperty); }
			set { SetValue(SnapOffsetYProperty, value); }
		}

		public static readonly DependencyProperty SnapOffsetYProperty = DependencyProperty.Register("SnapOffsetY", typeof(int), typeof(DesignerCanvas), new FrameworkPropertyMetadata(10, OnSnapOffsetYChanged));

		private static void OnSnapOffsetYChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{


			((DesignerCanvas)d).DrawGridLines(true);
		}
		#endregion




		private void DrawGridLines(bool removeLinesFirst)
		{
			if (removeLinesFirst)
			{
				int intTotalChildren = Children.Count - 1;
				for (int intCounter = intTotalChildren; intCounter > 0; intCounter--)
				{
					if (Children[intCounter].GetType() == typeof (Line))
					{
						var ucCurrentChild = Children[intCounter]  as Line;
						Children.Remove(ucCurrentChild);
					}
				}
			}

			var p = Parent as Border;

			// Create a red Brush
			var lineBrush = new SolidColorBrush();
			lineBrush.Color = Colors.Black;

			double xPos = SnapOffsetX;

			while (xPos < SystemParameters.PrimaryScreenWidth - SnapOffsetX)
			{
				var newLine = new Line();

				newLine.Tag = "V";
				newLine.X1 = xPos;
				newLine.Y1 = 0;
				newLine.X2 = xPos;
				newLine.Y2 = SystemParameters.PrimaryScreenHeight;

				newLine.SetValue(ZIndexProperty, 0);


				// Set Line's width and color
				newLine.StrokeThickness = 0.5;
				newLine.Stroke = lineBrush;
				newLine.StrokeDashArray = new DoubleCollection() { 2, 4 };

				if (ShowGridLines)
				{
					newLine.Visibility = Visibility.Visible;
				}
				else
				{
					newLine.Visibility = Visibility.Hidden;
				}
				Children.Add(newLine);

				xPos += SnapOffsetX;
			}

			double yPos = SnapOffsetY;

			while (yPos < SystemParameters.PrimaryScreenHeight - SnapOffsetY)
			{
				var newLine = new Line();

				newLine.Tag = "H";
				newLine.X1 = 0;
				newLine.Y1 = yPos;
				newLine.X2 = SystemParameters.PrimaryScreenWidth;
				newLine.Y2 = yPos;

				// Set Line's width and color
				newLine.StrokeThickness = 0.5;
				newLine.Stroke = lineBrush;
				newLine.StrokeDashArray = new DoubleCollection() { 2, 4 };

				if (ShowGridLines)
				{
					newLine.Visibility = Visibility.Visible;
				}
				else
				{
					newLine.Visibility = Visibility.Hidden;
				}
				Children.Add(newLine);

				yPos += SnapOffsetY;
			}
		}

		public void ResizeGrid()
		{
			IEnumerable<Line> collection = Children.OfType<Line>();

			foreach (var line in collection)
			{
				if (line.Tag.ToString() == "V")
				{
					line.Y2 = ActualHeight - 2;
				}
				if (line.Tag.ToString() == "H")
				{
					line.X2 = ActualWidth - 2;
				}
			}
		}

		#endregion

		public void DeselectAll()
		{
			foreach (DesignerItem item in SelectedItems)
			{
				item.IsSelected = false;

			}
		}

		#region Mouse Handling

		protected override void OnMouseDown(MouseButtonEventArgs e)
		{
			base.OnMouseDown(e);

			if (e.Source == this)
			{
				dragStartPoint = new Point?(e.GetPosition(this));
				DeselectAll();
				e.Handled = true;
			}

		}


		protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
		{
			base.OnPreviewMouseUp(e);

			//  Point position = e.GetPosition(this);

			// Console.WriteLine(@"Pos: {0} {1} {2} {3}", position.X, position.Y, Width, Height);

			foreach (var selectedItem in SelectedItems)
			{
				selectedItem.Control.IsDirty = true;

			}

		}


		protected override void OnMouseMove(MouseEventArgs e)
		{
			base.OnMouseMove(e);

			if (e.LeftButton != MouseButtonState.Pressed)
			{
				dragStartPoint = null;
			}

			if (dragStartPoint.HasValue)
			{
				AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
				if (adornerLayer != null)
				{
					var adorner = new RubberbandAdorner(this, dragStartPoint);

					adornerLayer.Add(adorner);

				}



				e.Handled = true;
			}
		}

		#endregion

		#region Clone
		private static FrameworkElement Clone(FrameworkElement e)
		{
			var document = new XmlDocument();
			document.LoadXml(XamlWriter.Save(e));

			return (FrameworkElement)XamlReader.Load(new XmlNodeReader(document));
		}

		#endregion

		private static int CalculateWidth(ControlViewModel parameter)
		{
			int width = 100;

			switch (parameter.ControlType)
			{
				case ControlTypeEnum.TextBox:
					width = 150;
					break;

				case ControlTypeEnum.NumericTextBox:
					width = 50;
					break;

				case ControlTypeEnum.DateTimePicker:
					width = 150;
					break;

				case ControlTypeEnum.DropDown:
					width = 50;
					break;

				case ControlTypeEnum.CheckBox:
					var formattedText = new FormattedText(parameter.Prompt,
								  CultureInfo.GetCultureInfo("en-us"),
								  FlowDirection.LeftToRight,
								  new Typeface("Verdana"),
								  12,
								  Brushes.Black);

					width = (int)formattedText.Width;
					break;


				default:
					width = 100;
					break;
			}

			return width;
		}

		public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
		{
			if (depObj != null)
			{
				for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
				{
					DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
					if (child != null && child is T)
					{
						yield return (T)child;
					}

					foreach (T childOfChild in FindVisualChildren<T>(child))
					{
						yield return childOfChild;
					}
				}
			}
		}

		#region Alignment
		public void AlignLeft()
		{
			if (SelectedItems.Count() < 2) return;

			int leftPosition = 5000;

			foreach (DesignerItem item in SelectedItems)
			{
				if (item.Control.LeftPosition != null)
				{
					if (item.Control.LeftPosition < leftPosition) leftPosition = (int) item.Control.LeftPosition;
				}

			}
			foreach (DesignerItem item in SelectedItems)
			{
				SetLeft(item, (double)leftPosition);

				item.Control.LeftPosition = leftPosition;

			}
		}

		public void AlignRight()
		{
			if (SelectedItems.Count() < 2) return;

			int rigthPosition = -5000;

			foreach (DesignerItem item in SelectedItems)
			{
				if (item.Control.LeftPosition != null)
				{
					if (item.Control.LeftPosition + item.Control.Width > rigthPosition) rigthPosition = (int)item.Control.LeftPosition + item.Control.Width;
				}

			}
			foreach (DesignerItem item in SelectedItems)
			{
				SetLeft(item, (double)rigthPosition - item.Width);

				item.Control.LeftPosition = (int)rigthPosition - (int)item.Width;

			}
		}

		public void AlignTop()
		{
			if (SelectedItems.Count() < 2) return;

			int topPosition = 5000;

			foreach (DesignerItem item in SelectedItems)
			{
				if (item.Control.TopPosition != null)
				{
					if (item.Control.TopPosition < topPosition) topPosition = (int)item.Control.TopPosition;
				}

			}
			foreach (DesignerItem item in SelectedItems)
			{
				SetTop(item, topPosition);

				item.Control.TopPosition = topPosition;

			}
		}

		public void AlignBottom()
		{
			if (SelectedItems.Count() < 2) return;

			int bottomPosition = -5000;

			foreach (DesignerItem item in SelectedItems)
			{
				if (item.Control.TopPosition != null)
				{
					if (item.Control.TopPosition + item.Control.Height > bottomPosition) bottomPosition = (int)item.Control.TopPosition + item.Control.Height;
				}

			}
			foreach (DesignerItem item in SelectedItems)
			{
				SetTop(item, (double)bottomPosition - item.Height);

				item.Control.TopPosition = (int)bottomPosition - (int)item.Height;

			}
		}

		public void AlignHorMiddles()
		{
			if (SelectedItems.Count() < 2) return;

			int topPosition = 5000;
			DesignerItem topmostItem = null;

			foreach (DesignerItem item in SelectedItems)
			{
				if (item.Control.TopPosition != null)
				{
					if (item.Control.TopPosition < topPosition)
					{
						topPosition = (int)item.Control.TopPosition;
						topmostItem = item;
					}
				}

			}

			int middlePosition = (int) topmostItem.Control.LeftPosition + topmostItem.Control.Width/2;

			foreach (DesignerItem item in SelectedItems)
			{
				SetLeft(item, middlePosition - item.Width / 2);

				item.Control.LeftPosition = (int)middlePosition - (int)item.Width / 2;

			}
		}

		public void AlignVerMiddles()
		{
			if (SelectedItems.Count() < 2) return;

			int leftPosition = 5000;
			DesignerItem leftmostItem = null;

			foreach (DesignerItem item in SelectedItems)
			{
				if (item.Control.LeftPosition != null)
				{
					if (item.Control.LeftPosition < leftPosition)
					{
						leftPosition = (int)item.Control.LeftPosition;
						leftmostItem = item;
					}
				}

			}

			int middlePosition = (int)leftmostItem.Control.TopPosition + leftmostItem.Control.Height / 2;

			foreach (DesignerItem item in SelectedItems)
			{
				SetTop(item, middlePosition - item.Height / 2);

				item.Control.TopPosition = (int)middlePosition - (int)item.Height / 2;

			}
		}
	#endregion

		#region "Serialization"
		public bool LoadFromXml(string fileName)
		{
			try
			{
				if (File.Exists(fileName))
				{
					Controls.CollectionChanged -= ItemsSourceCollectionChanged;

					Controls = SerializationHelper.DeserializeFromXml(fileName);
					Controls.CollectionChanged += ItemsSourceCollectionChanged;

					return true;
				}

				return false;
			}
			catch (Exception)
			{
				return false;
				
			}
		}

		public bool LoadFromBinary(string fileName)
		{
			try
			{
				if (File.Exists(fileName))
				{
					Controls.CollectionChanged -= ItemsSourceCollectionChanged;
					Controls = SerializationHelper.DeserializeFromBinary(fileName);

					Controls.CollectionChanged += ItemsSourceCollectionChanged;
					return true;
				}

				return false;
			}
			catch (Exception)
			{
				return false;

			}
		}

		public bool SaveToXml(string fileName)
		{
			try
			{
				SerializationHelper.SerializeToXml(Controls, fileName);

				return true;
			}
			catch (Exception)
			{
				return false;
			}
		}

		public bool SaveToBinary(string fileName)
		{
			try
			{
				SerializationHelper.SerializeToBinary(Controls, fileName);

				return true;
			}
			catch (Exception)
			{
				return false;
			}
		}
		#endregion
	}
}
